package vivace.model;

import java.util.Vector;
import java.util.Iterator;
import vivace.model.MusicalObject;
import vivace.helper.NoteHelper;

/**
 * Class modeling a bar (containing notes and paused) in the note view
 */
public class Bar {


    private Boolean[] displaySharp;

	private Vector<MusicalObject> objects;
	//private TimeSignature time;

    // the length of the bar in ticks
     private long length;

	
	// number of ticks contained in the bar
	private long tickCounter;
	

//    public Bar(long length, long resolution) {
//        this.length = length;
//        tickCounter = 0;
//        displaySharp = new Boolean[7];
//        for(int i =0;i<7;i++){
//            displaySharp[i]=true;
//        }
//		objects = new Vector<MusicalObject>();
//    }

	public Bar(TimeSignature time) {
        length = time.getLength(); //resolution * time.getNumerator();
		
    //  this.time = time;
        tickCounter = 0;
        displaySharp = new Boolean[7];
        for(int i =0;i<7;i++){
            displaySharp[i]=true;
        }
		objects = new Vector<MusicalObject>();
	}
	


    /**
     * Well, take a guess..
     */
    public boolean isEmpty(){
        return tickCounter == 0;
    }

    /**
     * Creates an empty bar with the beat 4/4
     */

    public Bar( long resolution ) {
        this(new TimeSignature(resolution));
    }

    /**
     * Returns all the MusicalObjectS contained within the bar as a Vector
     * @return
     */
	public Vector<MusicalObject> getObjects() {
		return objects;
	}

	/**
	 * Adds a MusicalObject to the bar
	 * @param o
	 */
	public void add(MusicalObject o){
        
        if (o.getLength() <= spaceLeft()){
            objects.add(o);
            NoteHelper.print("?",o);
            tickCounter += o.getLength();
        } else {
            
        }
    }

	/** 
	 * Adds a TimeSignature to the bar
	 * @param ts
	 */
    public void add(TimeSignature ts){
        objects.add(ts);
        NoteHelper.print("ts",ts);
    }


    private void _add(Note o) { 

        // Noter och pauser av längd noll lägges icke till.
        if (o.getLength() > 0) {
            if( o.getLength() <= spaceLeft()){
                int pos = o.getPosInOctave();
                // This is to tell if we want the note to be displayed as sharp when it is sharp, or as flat when it is flat.
                o.setDisplaySharp(displaySharp[pos]);
                // if we have a flat note, do not displaying flat signs for notes after the current one, and
                // if we have a sharp note, do not display sharp signs for notes after the current one.
                if(o.isSharp() == displaySharp[pos]) {
					displaySharp[pos] = !displaySharp[pos];
				}
               
                objects.add(o);
                o.setBar(this);
                tickCounter += o.getLength();
            } else {
            
            }
        }

    }

    private void _add(Pause p) {

        // Noter och pauser av längd noll lägges icke till.
        if (p.getLength() > 0) {
            if( p.getLength() <= spaceLeft() ){
                // Pauses with length equal to the whole bar should be displayed as a full pause.
                if( p.getLength() == length) {
					p.displayAsFull();
				}
                objects.add(p);
                tickCounter += p.getLength();
            } else {
            
            }
        }
    }


//    public void addN(MusicalObject o){
//        add(o);
//    }

    public void add(Note note){
        if (note.getLength() > 0) {
            if (note.getLength() <= spaceLeft()){
                Iterator<Note> noi = NoteHelper.normalize(note).iterator();
                for(Note n=noi.next();;n=noi.next()){
                    _add(n);
                    if(!noi.hasNext()) {
						break;
					}
                }
            }
        }
    }

    public void add(Pause pause){
        // If we have a pause as long as the bar just add it.
        if (pause.getLength() == length) {
			_add(pause);
		} else if (pause.getLength() > 0) {
            if( pause.getLength() <= spaceLeft()){
                Iterator<Pause> poi = NoteHelper.normalize(pause).iterator();
                for(Pause p=poi.next();;p=poi.next()){
                    _add(p);
                    if(!poi.hasNext()) {
						break;
					}
                }
            }
        }
    }


    /**
     * Removes a MusicalObject from the bar it belongs to.
     * In case of notes, the removal propagates to all the notes tied to the one being removed, independent of which Bar they belong to.
     * @param o the MusicalObject to remove.
     */

    public void remove(MusicalObject o) {
        objects.remove(o);
        tickCounter -= o.getLength();

    }

    
    public void remove(Note n){
        //First, we check to see if the note has a tie on either end.
        //If so, we remove those ties recursively.
        Note tiedFrom = n.getNoteTiedFrom() ; 
        // For effectiveness, I check if the assignment returns null rather than check if getNoteTiedFrom returns null and then assign.
        // If a tie exists from another note to this one, remove that tie and remove the other note, cascading through remaining ties.
        if (tiedFrom  != null){
            tiedFrom.removeTieTo(n);
            objects.remove(tiedFrom);
        }

        Note tiedTo = n.getNoteTiedTo(); 
        // For effectiveness, I check if the assignment returns null rather than check if getNoteTiedTo returns null and then assign.
        // If a tie exists from this note to another, remove that tie and remove the other note, cascading through remaining ties.
        if (tiedTo != null){
            n.removeTieTo(tiedTo);
            objects.remove(tiedTo);
        }

        // Now, we can finally remove the current note from its Bar.
        // That is, if it belongs to a bar at all. Otherwise it is Magically Forgotten by the Java Fairies of Garbage Collection Land.
        Bar babar = n.getBar();
        if (babar != null) { 
            babar.tickCounter -= n.getLength();
            babar.objects.remove(n); 
        }

    }


    public long spaceLeft() {
        //long ticks = resolution * time.getDenominator();
        //System.out.printf("%d = %d * %d", ticks, Project.RESOLUTION , time.getDenominator()); 

        return length - tickCounter;
    }

    public long getLength(){
        return length;
    }

    }
